//
// Created by 高曦祥 on 2023/11/26.
//

#ifndef TINY_DB_RW_LOCK_H
#define TINY_DB_RW_LOCK_H

#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include "util/base_types.h"

namespace TinyDB {

class RWLock {
 public:
  RWLock() : reader_cnt_(0), has_writer_(false) {};
  RWLock(const RWLock&) = delete;
  RWLock& operator=(const RWLock&) = delete;
  ~RWLock() = default;

  void RLock() {
    std::unique_lock<std::mutex> lock(mtx_);
    auto t_id = std::this_thread::get_id();
    que_.push(t_id);
    cv_.wait(lock, [&] { return que_.front() == t_id && !has_writer_; });
    reader_cnt_++;
    que_.pop();
  }

  void UnRLock() {
    std::unique_lock<std::mutex> lock(mtx_);
    reader_cnt_--;
    if (reader_cnt_ == 0) {
      cv_.notify_all();
    }
  }

  void WLock() {
    std::unique_lock<std::mutex> lock(mtx_);
    auto t_id = std::this_thread::get_id();
    que_.push(t_id);
    cv_.wait(lock, [&] {return que_.front() == t_id && !has_writer_ && reader_cnt_ == 0; });
    has_writer_ = true;
    que_.pop();
  }

  void UnWLock() {
    std::unique_lock<std::mutex> lock(mtx_);
    has_writer_ = false;
    cv_.notify_all();
  }

  t_uint32 GetReadCnt() { return reader_cnt_; }
  t_bool HasWriter() { return has_writer_; }

 private:
  std::mutex mtx_;
  std::condition_variable cv_;
  std::queue<std::thread::id> que_;
  t_uint32 reader_cnt_;
  t_bool has_writer_;
};

}

#endif //TINY_DB_RW_LOCK_H
